
#include <string.h>
#include "i7816_config.h"
#include "i7816_log.h"
#include "i7816_status.h"
#include "i7816_define.h"
#include "i7816_utils.h"
#include "i7816_hal.h"

#define is_TA_present(TD) (TD & 0x10)
#define is_TB_present(TD) (TD & 0x20)
#define is_TC_present(TD) (TD & 0x40)
#define is_TD_present(TD) (TD & 0x80)
#define is_TCK_present(TD) (((TD) & 0x0F) != 0)

typedef enum _i7816_interface_byte{
	IFB_TA,
	IFB_TB,
	IFB_TC,
	IFB_TD
}i7816_interface_byte_t;

/*=================================================接收ATR=======================================================*/

static uint8_t get_lenght_to_receive_from(uint8_t TD)
{	
    return i7816_nibble_setbit_number(TD >> 4);
}

static i7816_status_t i7816_receive_atr_byte(i7816_hw_t* hw, uint8_t *byte, uint32_t clk)
{
    i7816_status_t status;
    uint16_t data = 0;    

    status = i7816_hal_receive(hw, &data, clk);
    if(status != I7816_S_OK){
        return status;
    }
	
	if(hw->param->inverse){
		data = i7816_to_inverse(data);
	}

    *byte = (uint8_t)data;
    return status;
}

static i7816_status_t i7816_receive_atr_block(i7816_hw_t* hw, uint8_t block[], uint8_t block_len, uint32_t clk)
{
    uint8_t i;
    i7816_status_t status = I7816_S_OK;

    for(i = 0; i < block_len; i++)
    {
        status = i7816_receive_atr_byte(hw, block + i, clk);
        if(status != I7816_S_OK)
        {
            break;
        }
    }

    return status;
}


static i7816_status_t i7816_receive_ts(i7816_hw_t* hw, uint8_t *ts, uint32_t wait_time)
{
	i7816_status_t status;
	uint16_t rx;
	status = i7816_hal_receive(hw, &rx, wait_time);
	if(status != I7816_S_OK){
		return status;
	}

	*ts = rx;
	
	if(*ts == 0x3B){
		return I7816_S_OK;
	}

    if(*ts == 0x03){
		*ts = 0x3F;
		hw->param->inverse = true;
		return I7816_S_OK;
	}
	I7816_LOG_ERR("invalid ts = %02X", *ts);
	return I7816_S_TS;
}
//接收ATR
static i7816_status_t i7816_receive_atr(i7816_ifd_t *ifd){
    i7816_status_t status = I7816_S_OK;
    uint8_t rx_len = 0;
    uint8_t history_len;
    uint8_t TCK_len = 0;
    uint8_t TD;
    uint8_t T0;
    uint8_t len_to_receive;
    uint8_t FiDi = 0x11;
	uint32_t WWT = i7816_etu2clk(9600, FiDi);
	i7816_hw_t* hw = ifd->hw;
	i7816_icc_t* icc = &ifd->icc;
	uint8_t *atr = icc->atr;

 	icc->alen = 0;

    /*
    The terminal shall have a reception window which is opened no later than
    380 clock cycles after time T1 and closed no earlier than 42,000 clock cycles
    after time T1.
    42000clock + 12etu = waittime, because we check the end of the char.
    */
    status = i7816_receive_ts(hw, atr, 42000);
    if(status != I7816_S_OK){
		I7816_LOG_ERR("receive ts fail: %d", status);
        return I7816_S_TS;
    }

    /*
    The terminal shall be able to receive an ATR having a duration of less than or
    equal to 20,160 initial etus.
    */

    rx_len = 1;
	status = i7816_receive_atr_byte(hw, atr + rx_len, WWT);
    if(status != I7816_S_OK){
	   I7816_LOG_ERR("receive t0 failL %d", status);
       return status;
    }

    T0 = atr[rx_len];
    ++rx_len;
    history_len = T0 & 0x0F;
    TD = T0 & 0xF0;

    while((len_to_receive = get_lenght_to_receive_from(TD)) != 0)
    {
        status = i7816_receive_atr_block(hw, atr + rx_len, len_to_receive, WWT);

        if(status != I7816_S_OK)
        {
			I7816_LOG_ERR("receive inter byte fail: %d", status);
            return status;
        }

        rx_len += len_to_receive;

        if(is_TD_present(TD))
        {
            TD = atr[rx_len - 1];

            if(is_TCK_present(TD))
            {
                TCK_len = 1;
            }
        }
        else
        {
            break;
        }

    }

    status = i7816_receive_atr_block(hw, atr + rx_len, history_len, WWT);

    if(status != I7816_S_OK)
    {
		I7816_LOG_ERR("receive history fail: %d", status);
		return status;
    }

    rx_len += history_len;

    if(TCK_len != 0)
    {
        status = i7816_receive_atr_byte(hw, atr + rx_len, WWT);

        if((status != I7816_S_OK))
        {
			I7816_LOG_ERR("receive tck fail: %d", status);
            return status;
        }
        /*
        The value of TCK is such that the exclusive-OR ing of all bytes from T0
        to TCK inclusive is null
        */
	   	uint8_t tck = atr[rx_len];
		uint8_t xor = i7816_xor_sequence(atr+1, rx_len-1);
		++rx_len;
		if(tck != xor){
			I7816_LOG_ERR("tck error: tck=%02X, xor=%02X", tck, xor);
			I7816_LOG_BUF("atr ", atr, rx_len);
			return I7816_S_TCK;
		}
    }

    icc->alen = rx_len;

    return status;
}

/*=================================================解析ATR=======================================================*/
int i7816_atr_get_byte_pos(const uint8_t* atr, i7816_interface_byte_t ifb, uint8_t index){
	uint8_t TD;
	uint8_t offset;	
	uint8_t i = 1;
	uint8_t len;

	TD = atr[1];
	offset = 2;
	
	while(i < index)
	{
		if((TD & 0xF0) == 0){
			return -1;
		}

		if(is_TA_present(TD))
		{
			offset++;
		}

		if(is_TB_present(TD))
		{
			offset++;
		}

		if(is_TC_present(TD))
		{
			offset++;
		}
		
		if(is_TD_present(TD))
		{
			TD = atr[offset];
			offset++;
		}
		else
		{
			return -1;
		}
		i++;
	}

	if((TD & 0xF0) == 0){
		return -1;
	}

	if(is_TA_present(TD))
	{
		if(ifb == IFB_TA){
			return offset;
		}
		offset++;
	}

	if(is_TB_present(TD))
	{
		if(ifb == IFB_TB){
			return offset;
		}
		offset++;
	}

	if(is_TC_present(TD))
	{
		if(ifb == IFB_TC){
			return offset;
		}
		offset++;
	}
	
	if(is_TD_present(TD))
	{
		if(ifb == IFB_TD){
			return offset;
		}
	}

	return -1;
}

static bool i7816_atr_get_byte(const uint8_t* atr, uint8_t alen, i7816_interface_byte_t ifb, uint8_t index, uint8_t* val){
	int offset = i7816_atr_get_byte_pos(atr, ifb, index);
	if(offset < 0){
		return false;
	}
	if(offset >= alen){
		return false;
	}
	if(val){
		*val = atr[offset];
	}
	return true;
}

static uint8_t i7816_atr_get_byte_or_default(const uint8_t* atr, uint8_t alen, i7816_interface_byte_t ifb, uint8_t index, uint8_t default_val){
	uint8_t val = default_val;
	i7816_atr_get_byte(atr, alen, ifb, index, &val);
	return val;
}

static i7816_status_t i7816_verify_atr(i7816_ifd_t *ifd){
/*
ISO/IEC 7816-3:2006(E):
An interface device supporting the three values F, D and T referenced by TA2 should initiate the transmission 
protocol T with F and D. Otherwise, it should perform either a warm reset (bit 8 set to 0) or a deactivation (bit 8 set to 1).
*/	
	uint8_t* atr = ifd->icc.atr;
	uint8_t alen = ifd->icc.alen;
	uint8_t ta2;	

	if(i7816_atr_get_byte(atr, alen, IFB_TA, 2, &ta2)){
		/*
		TA2 is present,Card in specific mode. 
			A card transmitting character TA2 to an interface device not aware of the existence of specific mode cannot rely 
		on a warm reset to switch the mode.
			An interface device having detected character TA2 should not initiate a warm reset before it detects either an 
		unsupported value in the received characters, or an overrun of WT (see 7.2).
		*/
		uint8_t T = ta2 & 0x0F;
		if(T != 1 || T != 0){
			if(ta2 & 0x80){
				//unable to change if bit 8 is set to 1.
				return I7816_S_ABORT;
			}
			else{
				//capable to change if bit 8 is set to 0;
				return I7816_S_REJECT_ATR;
			}
		}
		ifd->param.protocol = (T != 0) ? PROTOCOL_T1 : PROTOCOL_T0;
		if(ta2 & (1 << 4)){
			//If bit 5 is set to 1, then implicit values (not defined by the interface bytes) shall apply.
			ifd->param.FiDi = 0x11;
		}
		else{
			//If bit 5 is set to 0, then the integers Fi and Di defined above by TA1 shall apply.
			ifd->param.FiDi = i7816_atr_get_byte_or_default(atr, alen, IFB_TA, 1, 0x11);
		}
	}
	else{
		/*
		Card in negotiable mode.
		The interface device shall do so when the card offers only one transmission protocol and only 
		the default values of the transmission parameters.
		*/
		//获取TD1，检查first offered transmission protocol
		uint8_t td1 = i7816_atr_get_byte_or_default(atr, alen, IFB_TD, 1, 0x00);
		if((td1 & 0x0F) == 0x0F){
			return I7816_S_REJECT_ATR;
		}
	}

	return I7816_S_OK;
}

static bool i7816_atr_check_t_present(const uint8_t* atr, uint8_t alen, uint8_t idx, uint8_t T){
	uint8_t td;
	uint8_t i = idx;
	while(i7816_atr_get_byte(atr, alen, IFB_TD, i, &td)){
		if((td & 0x0F) == T){
			return true;
		}
		i++;
	}
	return false;
}

static bool i7816_atr_check_t15(const uint8_t* atr, uint8_t alen){
	return i7816_atr_check_t_present(atr, alen, 2, 15);
}

static bool i7816_atr_check_t1(const uint8_t* atr, uint8_t alen){
	return i7816_atr_check_t_present(atr, alen, 0, 1);
}
/*==============================================================================================================*/

static uint32_t i7816_atr_parse_wwt(uint8_t* atr, uint8_t alen, uint8_t FiDi){
	/**
	 *  WT = WI × 960× Fi / f
	 */
	uint8_t tc2 = i7816_atr_get_byte_or_default(atr, alen, IFB_TC, 2, 10);
	if(tc2 == 0){
		tc2 = 10;
	}
	
	uint8_t Fi = FiDi >> 4;
	uint32_t etu = tc2 * 960 * Fi;
	
	return i7816_etu2clk(etu, FiDi);
}
/*==============================================================================================================*/

static void i7816_atr_parse_cwt_bwt(uint8_t* atr, uint8_t alen, uint8_t FiDi, uint32_t *cwt, uint32_t* bwt){
	uint8_t cwi,bwi;
	uint8_t tb3 = i7816_atr_get_byte_or_default(atr, alen, IFB_TB, 3, (4 << 4) | 13);

	cwi = tb3 & 0x0F;
	bwi = (tb3 >> 4) & 0x0F;
	if(bwi > 9){
		bwi = 9;
	}
	//CWT = (11+ 2^CWI ) etu
	uint32_t etu = 11 + (1 << cwi);
//		*cwt = i7816_etu2clk(etu, FiDi);
	*cwt = i7816_etu2clk(etu, 0x11);
	//BWT = 11etu + 2^BWI × 960 × Fd / f.  Fd -- default values of F
	etu = 11 + (1 << bwi) * 960;
	*bwt = i7816_etu2clk(etu, 0x11);
}
/*==============================================================================================================*/
static uint8_t i7816_atr_parse_ifsc(uint8_t* atr, uint8_t alen){
	
	uint8_t ta3 = i7816_atr_get_byte_or_default(atr, alen, IFB_TA, 3, 32);
	if(ta3 == 0 || ta3 == 0xFF){
		return 32;
	}
	return ta3;
}

/*==============================================================================================================*/
static i7816_guard_time_t i7816_atr_parse_guard_time(uint8_t* atr, uint8_t alen, uint8_t FiDi){
	uint8_t tc1 = i7816_atr_get_byte_or_default(atr, alen, IFB_TC, 1, 0x00);
	i7816_guard_time_t guard_time;
	/*
	GT = 12etu + R * N / f
	• If T=15 is absent in the Answer-to-Reset, then R = F / D, i.e., the integers used for computing the etu. 
	• If T=15 is present in the Answer-to-Reset, then R = Fi / Di, i.e., the integers defined above by TA1.
	*/
	/*
	默认 T0: GT = 12etu， T1: GT = 11etu
	*/
	guard_time.etu = 0;
	if(tc1 == 0xFF){
		guard_time.extra_clk = 0;
	}
	else{		
		guard_time.extra_clk = i7816_etu2clk(tc1, FiDi);
	}

	return guard_time;
}

/*==============================================================================================================*/

static void i7816_hw_param_init(i7816_hw_param_t* param){
	param->FiDi = FIDI_DEFAULT_VAL;
	param->GT.extra_clk = 0;
	param->GT.etu = 0;
	param->inverse = false;
	param->type = ICC_CLASS_B;
	param->protocol = PROTOCOL_ANY;
}

static void i7816_icc_init(i7816_icc_t* icc){
	(void)memset(icc, 0, sizeof(*icc));
}


static i7816_status_t i7816_reset(i7816_ifd_t *ifd, i7816_class_t type, reset_mode_t mode){
	i7816_hal_setup(ifd->hw, TRANSFER_IN);
	i7816_hal_reset(ifd->hw, type, mode);
	// i7816_hal_flush(ifd->hw);
	return i7816_receive_atr(ifd);
}

static i7816_status_t i7816_class_selection(i7816_ifd_t *ifd, uint8_t type_select){
	i7816_status_t status = I7816_S_ABORT;		
	static const i7816_class_t types[3] = {ICC_CLASS_B, ICC_CLASS_A, ICC_CLASS_C};

	for(int i = 0; i < 3; i++){
		i7816_class_t type = types[i];
		if((type_select & type) == 0){
			continue;
		}
		I7816_LOG_DBG("select class: %d", type);
		status = i7816_reset(ifd, type, RESET_MODE_COLD);
		if(status != I7816_S_TS){
			//the card Answer-to-Reset, select this type
			ifd->param.type = type;
			I7816_LOG_BUF("atr ", ifd->icc.atr, ifd->icc.alen);
			return status;
		}
		// the card does not answer to reset, after a delay of at least 10 ms, apply another class
	}
	I7816_LOG_ERR("fail: %d", status);
	return I7816_S_ABORT;
}

static bool i7816_is_pps_need(i7816_ifd_t *ifd){
	return ifd->param.protocol == PROTOCOL_ANY;
}
/*
The PPS exchange shall start as specified in 6.3.1. The character frame shall be as specified in 7.1 and 7.2, 
using the coding convention fixed by TS (see 8.1), with GT as specified in 8.3 and WT = 9 600 etu. The error 
signal and character repetition according to 7.3 is mandatory for the cards offering T=0;
*/
static void i7816_pps_request(i7816_ifd_t *ifd, uint8_t* req, uint8_t len){
	i7816_hal_delay_guard_time(ifd->hw, i7816_etu2clk(6, FIDI_DEFAULT_VAL));
	i7816_hal_setup(ifd->hw, TRANSFER_OUT);
	for(int i = 0; i < len; i++){
		i7816_hal_send(ifd->hw, req[i]);
	}
	i7816_hal_flush(ifd->hw, TRANSFER_OUT);	
}

//pps接收一个字节
static i7816_status_t i7816_pps_receive_byte(i7816_ifd_t *ifd, uint8_t* byte){
	i7816_status_t status = I7816_S_ABORT;
	uint16_t data = 0;
	uint32_t wclk = i7816_etu2clk(9600, FIDI_DEFAULT_VAL);
	status = i7816_hal_receive(ifd->hw, &data, wclk);
	if(status != I7816_S_OK){
		return status;
	}
	if(ifd->hw->param->inverse){
		data = i7816_to_inverse(data);
	}
	*byte = data;
	return status;
}

static i7816_status_t i7816_pps_response(i7816_ifd_t *ifd, uint8_t* resp, uint8_t resp_len){
	i7816_status_t status = I7816_S_ABORT;
	//接收起始字节FF
	while(1){
		status = i7816_pps_receive_byte(ifd, &resp[0]);
		if(status != I7816_S_OK){
			return status;
		}
		if(resp[0] == 0xFF){
			break;
		}
		LOG2_WRN("pps resp wrong stx: %02X", resp[0]);
	}

	for(int i = 1; i < resp_len; i++){
		status = i7816_pps_receive_byte(ifd, &resp[i]);
		if(status != I7816_S_OK){
			return status;
		}
	}

	return I7816_S_OK;
}

static i7816_status_t i7816_pps_exchange(i7816_ifd_t *ifd, uint8_t* req, uint8_t req_len, uint8_t* resp){
/*
 the default values of the transmission parameters shall continue to apply until completion of a successful PPS exchange (see 9.3)
*/
	ifd->param.protocol = PROTOCOL_T0;
	i7816_hal_config(ifd->hw, &ifd->param);
	i7816_pps_request(ifd, req, req_len);
	return i7816_pps_response(ifd, resp, req_len);
}

#if I7816_CFG_PPS
static i7816_status_t i7816_pps(i7816_ifd_t *ifd){
	uint8_t FiDi = ifd->param.FiDi;
	i7816_protocol_t T = ifd->param.protocol;

	if(T == PROTOCOL_ANY){
		uint8_t *atr = ifd->icc.atr;
		uint8_t alen = ifd->icc.alen;
		T = i7816_atr_check_t1(atr, alen) ? PROTOCOL_T1 : PROTOCOL_T0;
//			FiDi = i7816_atr_get_byte_or_default(atr, alen, IFB_TA, 1, FIDI_DEFAULT_VAL);
//			 FiDi = 0x86;	//为提高传输速率，使用Mifare SAM AV的非标准FiDi，F/D = 4
		FiDi = 0x97;
	}
	
	uint8_t req_len = 0;
	uint8_t req[4];
	uint8_t resp[4];

	req[req_len++] = 0xFF;
	req[req_len++] = (T == PROTOCOL_T0) ? 0x10 : 0x11;
	req[req_len++] = FiDi;
	req[req_len] = i7816_xor_sequence(req, req_len);
	req_len++;
	I7816_LOG_BUF("pps req", req, req_len);
	i7816_status_t status = i7816_pps_exchange(ifd, req, req_len, resp);
	if(status != I7816_S_OK){
		I7816_LOG_ERR("pps fail: %d", status);
		return status;
	}
	I7816_LOG_BUF("pps resp", resp, req_len);
	if(memcmp(req, resp, req_len)){
		return I7816_S_ABORT;
	}
	ifd->param.FiDi = FiDi;
	ifd->param.protocol = T;
	return status;
}
#endif 

static i7816_status_t i7816_ifd_setup(i7816_ifd_t *ifd){
	i7816_icc_t* icc = &ifd->icc;
	uint8_t* atr = icc->atr;
	uint8_t alen = icc->alen;
	i7816_hw_param_t* param = &ifd->param;
	uint8_t FiDi = param->FiDi;
	
	if(param->protocol == PROTOCOL_ANY){
		param->protocol = i7816_atr_check_t1(atr, alen) ? PROTOCOL_T1 : PROTOCOL_T0;
	}

	if(param->protocol == PROTOCOL_T0){
		icc->WWT = i7816_atr_parse_wwt(atr, alen, FiDi);
	}
	else{
		/*
		CWT,BWT
		*/
		uint32_t cwt,bwt;
		i7816_atr_parse_cwt_bwt(atr, alen, FiDi, &cwt, &bwt);
		icc->CWT = cwt;
		icc->BWT = bwt;
		/*IFSC*/
		icc->IFSC = i7816_atr_parse_ifsc(atr, alen);
	}
	/*
	GT = 12etu + R * N / f
	• If T=15 is absent in the Answer-to-Reset, then R = F / D, i.e., the integers used for computing the etu. 
	• If T=15 is present in the Answer-to-Reset, then R = Fi / Di, i.e., the integers defined above by TA1.
	*/
	uint8_t tfidi = FiDi;
	if(i7816_atr_check_t15(atr, alen)){
		tfidi = i7816_atr_get_byte_or_default(atr, alen, IFB_TA, 1, FIDI_DEFAULT_VAL);
	}
	param->GT = i7816_atr_parse_guard_time(atr, alen, tfidi);
	return I7816_S_OK;
}


static i7816_status_t i7816_parse_atr(i7816_ifd_t *ifd){
	i7816_status_t status = I7816_S_ABORT;
	uint8_t* atr = ifd->icc.atr;
	uint8_t alen = ifd->icc.alen;
	uint8_t ta2 = 0;	
	uint8_t td1 = i7816_atr_get_byte_or_default(atr, alen, IFB_TD, 1, 0x00);
	uint8_t ta1 = i7816_atr_get_byte_or_default(atr, alen, IFB_TA, 1, 0x11);
	int ta2_pos = i7816_atr_get_byte_pos(atr, IFB_TA, 2);
	
	uint8_t T;
	uint8_t FiDi;

	if(ta2_pos > 0){
		ta2 = atr[ta2_pos];
		T = ta2 & 0x0F;
		if(T != 1 || T != 0){
			if(ta2 & 0x80){
				//unable to change if bit 8 is set to 1.
				return I7816_S_ABORT;
			}
			else{
				//capable to change if bit 8 is set to 0;
				return I7816_S_REJECT_ATR;
			}
		}		
		if(ta2 & (1 << 4)){
			//If bit 5 is set to 1, then implicit values (not defined by the interface bytes) shall apply.
			FiDi = 0x11;
		}
		else{
			//If bit 5 is set to 0, then the integers Fi and Di defined above by TA1 shall apply.
			FiDi = ta1;
		}
		ifd->param.FiDi = FiDi;
		ifd->param.protocol = (T != 0) ? PROTOCOL_T1 : PROTOCOL_T0;
	}
	

	return I7816_S_OK;
}

static i7816_status_t i7816_do_activate(i7816_ifd_t *ifd){
	i7816_status_t status = I7816_S_POWER_UP;
	static const i7816_class_t types[3] = {ICC_CLASS_B, ICC_CLASS_A, ICC_CLASS_C};
	uint8_t type_select = i7816_hal_get_support_type(ifd->hw);
	status = i7816_class_selection(ifd, type_select);
	if(status == I7816_S_ABORT){
		return status;
	}

	if(status != I7816_S_OK){
		//接收ATR错误，热复位，重新接收
		I7816_LOG_WARN("warm reset");
		status = i7816_reset(ifd, ifd->param.type, RESET_MODE_WARM);
		if(status != I7816_S_OK){
			I7816_LOG_ERR("warm reset fail: %d", status);
			return status;
		}
	}
	//解析ATR
	status = i7816_verify_atr(ifd);
	if(status == I7816_S_REJECT_ATR){
		//不支持的参数，热复位后重新接收ATR
		I7816_LOG_WARN("warm reset");
		status = i7816_reset(ifd, ifd->param.type, RESET_MODE_WARM);
		if(status != I7816_S_OK){
			I7816_LOG_ERR("warm reset fail: %d", status);
			return status;
		}
		status = i7816_verify_atr(ifd);
	}

	return status;
}

i7816_status_t  i7816_activate(i7816_ifd_t *ifd)
{
	i7816_status_t status;

	i7816_icc_init(&ifd->icc);
	i7816_hw_param_init(&ifd->param);
	i7816_hal_config(ifd->hw, &ifd->param);

	status = i7816_do_activate(ifd);
#if I7816_CFG_PPS
	if(status == I7816_S_OK && i7816_is_pps_need(ifd)){
 		status = i7816_pps(ifd);
	}
#endif	
	if(status == I7816_S_OK){
		status = i7816_ifd_setup(ifd);
	}
	
	if(status != I7816_S_OK){
		i7816_hal_deactive(ifd->hw);
	}

	return status;
}

void i7816_deactivate(i7816_ifd_t *ifd){
	i7816_hal_select(ifd->hw);
	i7816_hal_deactive(ifd->hw);
}

